package storage

import (
	"fmt"
	"mini-sql/model"
	"sync"
	"unsafe"
)

type MemStore struct {
	mutex     sync.Mutex
	TableName string
	MaxRowID  int64
	RowIds    []int64
	RowMap    map[int64]*model.TableTuple // id -> row
}

// GetStat implements StoreInterface.
func (m *MemStore) GetStat() *StoreStat {
	int64Value := int64(0)
	memUsed := int64(0)
	memUsed += int64(unsafe.Sizeof(m.RowIds)) + int64(unsafe.Sizeof(int64Value))*int64(cap(m.RowIds))
	memUsed += int64(unsafe.Sizeof(m.RowMap))
	for rowId, tuple := range m.RowMap {
		memUsed += int64(unsafe.Sizeof(rowId))
		memUsed += int64(unsafe.Sizeof(*tuple))
		for _, field := range tuple.Fields {
			memUsed += int64(unsafe.Sizeof(field))
		}
	}
	return &StoreStat{
		MemUsed: memUsed,
	}
}

type MemStoreIterator struct {
	tableStore *MemStore
	scanIndex  int
}

func (m *MemStore) DeleteRow(tuple model.TableTuple) error {
	m.mutex.Lock()
	defer func() {
		m.mutex.Unlock()
	}()
	newRowIds := make([]int64, 0, len(m.RowIds))
	for _, rowId := range m.RowIds {
		if rowId != tuple.RowID {
			newRowIds = append(newRowIds, rowId)
		}
	}
	m.RowIds = newRowIds
	delete(m.RowMap, tuple.RowID)
	return nil
}

func (m *MemStore) UpdateRow(tuple model.TableTuple) error {
	m.mutex.Lock()
	defer func() {
		m.mutex.Unlock()
	}()
	m.RowMap[tuple.RowID] = &tuple
	return nil
}

func (m *MemStore) GetRow(rowId int64) *model.TableTuple {
	result := m.RowMap[rowId]
	if result == nil {
		return result
	}
	copy := *result
	return &copy
}

func (m *MemStore) UpdateRowID(rowID int64) error {
	m.mutex.Lock()
	defer func() {
		m.mutex.Unlock()
	}()
	if rowID > m.MaxRowID {
		m.MaxRowID = rowID
	}
	return nil
}

func (m *MemStore) CreateRowID() (int64, error) {
	m.mutex.Lock()
	defer func() {
		m.mutex.Unlock()
	}()
	newId := m.MaxRowID + 1
	m.MaxRowID = newId
	return newId, nil
}

func (s *MemStore) InsertRow(row model.TableTuple) error {
	if row.RowID == 0 {
		return fmt.Errorf("invalid RowID 0")
	}
	exist := s.RowMap[row.RowID]
	if exist != nil {
		return fmt.Errorf("RowID %v conflict", row.RowID)
	}
	s.RowIds = append(s.RowIds, row.RowID)
	s.RowMap[row.RowID] = &row
	return nil
}

func (s *MemStore) GetIterator() StoreIterator {
	return &MemStoreIterator{
		tableStore: s,
	}
}

func (p *MemStoreIterator) GetNextTuple() (*model.TableTuple, error) {
	for p.scanIndex < len(p.tableStore.RowIds) {
		rowId := p.tableStore.RowIds[p.scanIndex]
		row := p.tableStore.GetRow(rowId)
		p.scanIndex += 1
		return row, nil

	}
	return nil, nil
}

func NewMemStore() StoreInterface {
	result := MemStore{}
	result.RowIds = make([]int64, 0)
	result.RowMap = make(map[int64]*model.TableTuple)
	return &result
}
